#include "mesh_renderer1.h"
#include "vortex/core/asset.h"
#include <glm/glm.hpp>
#include <iostream>

namespace vortex
{
    MeshRenderer1::MeshRenderer1()
    {
        std::map<vortex::ShaderType, std::string> shader_sources;
        std::string vspath = get_asset_path("shaders/mesh1.vs");
        std::string fspath = get_asset_path("shaders/mesh1.fs");
        // std::cout << vspath << std::endl;
        // std::cout << fspath << std::endl;
        shader_sources.insert(std::make_pair(vortex::ShaderType::Vertex, vspath));
        shader_sources.insert(std::make_pair(vortex::ShaderType::Fragment, fspath));
        shader = std::make_shared<ShaderProgram>(shader_sources);

        // create vao
        glGenVertexArrays(1, &vao);
        glBindVertexArray(vao);

        glGenBuffers(1, &vbo_position);
        glBindBuffer(GL_ARRAY_BUFFER, vbo_position);
        glBufferData(GL_ARRAY_BUFFER, MAX_VERTICES * sizeof(glm::vec3), nullptr, GL_DYNAMIC_DRAW);
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(glm::vec3), (void*)0);
        glEnableVertexAttribArray(0);

        glGenBuffers(1, &vbo_normal);
        glBindBuffer(GL_ARRAY_BUFFER, vbo_normal);
        glBufferData(GL_ARRAY_BUFFER, MAX_VERTICES * sizeof(glm::vec3), nullptr, GL_DYNAMIC_DRAW);
        glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(glm::vec3), (void*)0);
        glEnableVertexAttribArray(1);

        glGenBuffers(1, &vbo_uv);
        glBindBuffer(GL_ARRAY_BUFFER, vbo_uv);
        glBufferData(GL_ARRAY_BUFFER, MAX_VERTICES * sizeof(glm::vec2), nullptr, GL_DYNAMIC_DRAW);
        glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(glm::vec2), (void*)0);
        glEnableVertexAttribArray(2);

#if 0
        glGenBuffers(1, &ibo);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, MAX_VERTICES * sizeof(uint16_t), nullptr, GL_DYNAMIC_DRAW);
#endif  
        glBindVertexArray(0);
    }

    MeshRenderer1::~MeshRenderer1()
    {
        if (vao != 0) glDeleteVertexArrays(1, &vao);
        // vbos
    }

    void MeshRenderer1::Begin(const Environment3D& env)
    {
#if 1
        glDisable(GL_CULL_FACE);
        // glFrontFace(GL_CCW);

        glDepthMask(GL_TRUE);
        glEnable(GL_DEPTH_TEST);
        glDepthFunc(GL_LEQUAL);

        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
#endif

        shader->Bind();
        
        // set uniforms
        glm::mat4 view = env.camera->getViewMatrix();
        glm::mat4 proj = env.camera->getProjectionMatrix(env.window_width, env.window_height);
        glm::mat4 vp = proj * view;
        shader->SetUniformMat4("mvp", vp);
    }

    void MeshRenderer1::End()
    {
        // do nothing.
    }

    void MeshRenderer1::Draw(const Mesh1& mesh)
    {
        glBindVertexArray(vao);

        glBindBuffer(GL_ARRAY_BUFFER, vbo_position);
        glBufferSubData(GL_ARRAY_BUFFER, 0, mesh.num_vertices * sizeof(glm::vec3), mesh.vp);

        glBindBuffer(GL_ARRAY_BUFFER, vbo_normal);
        glBufferSubData(GL_ARRAY_BUFFER, 0, mesh.num_vertices * sizeof(glm::vec3), mesh.vn);

        glBindBuffer(GL_ARRAY_BUFFER, vbo_uv);
        glBufferSubData(GL_ARRAY_BUFFER, 0, mesh.num_vertices * sizeof(glm::vec2), mesh.vt);
        glDrawArrays(GL_TRIANGLES, 0, mesh.num_vertices);
    }
}
